use std::collections::HashMap;

use serde::{Deserialize, Deserializer, Serialize};

use crate::structs::{report::AuthFilter, DataType};
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ContextMenus {
    pub items: Vec<ContextMenuItem>,
}
impl ContextMenus {
    pub fn new() -> Self {
        ContextMenus { items: vec![] }
    }
    pub fn push(&mut self, item: ContextMenuItem) {
        self.items.push(item);
    }
    pub fn is_empty(&self) -> bool {
        self.items.is_empty()
    }
    pub fn get_context_menu_item(&self, index: usize) -> Option<&ContextMenuItem> {
        self.items.get(index)
    }
    pub fn get_default_action_index(&self, col_name: &str, data_type: DataType) -> Option<usize> {
        for (i, item) in self.items.iter().enumerate() {
            match item.trigger.mode {
                super::TriggerMode::Row | super::TriggerMode::Col => {}
                super::TriggerMode::AllNumCell => {
                    if data_type == DataType::Int || data_type == DataType::Float {
                        return Some(i);
                    }
                }
                super::TriggerMode::CellInCols => {
                    if item.trigger.col_name_where_map.contains_key(col_name) {
                        return Some(i);
                    }
                }
            }
        }
        None
    }
    pub fn get_row_context_menu(&self) -> Option<usize> {
        for (index, item) in self.items.iter().enumerate() {
            if item.trigger.mode == super::TriggerMode::Row {
                return Some(index);
            }
        }
        None
    }
    pub fn get_cell_context_menu_list(
        &self,
        col_name: &str,
        data_type: DataType,
    ) -> Vec<(usize, String, bool)> {
        let mut items = Vec::new();
        for (index, item) in self.items.iter().enumerate() {
            match item.trigger.mode {
                super::TriggerMode::Row | super::TriggerMode::Col => {}
                super::TriggerMode::AllNumCell => {
                    if data_type == DataType::Int || data_type == DataType::Float {
                        items.push((index, item.caption.clone(), item.is_default));
                    }
                }
                super::TriggerMode::CellInCols => {
                    if item.trigger.col_name_where_map.contains_key(col_name) {
                        items.push((index, item.caption.clone(), item.is_default));
                    }
                }
            }
        }
        items
    }
    pub fn get_col_context_menu_list(
        &self,
        col_name: &str,
    ) -> Vec<(usize, String)> {
        let mut items = Vec::new();
        for (index, item) in self.items.iter().enumerate() {
            match item.trigger.mode {
                super::TriggerMode::Row | super::TriggerMode::AllNumCell | super::TriggerMode::CellInCols => {}
                super::TriggerMode::Col => {
                    if item.trigger.col_name_where_map.contains_key(col_name) {
                        items.push((index, item.caption.clone()));
                    }
                }
            }
        }
        items
    }
    pub fn has_cell_context_menus(&self) -> bool {
        for item in self.items.iter() {
            match item.trigger.mode {
                super::TriggerMode::Row | super::TriggerMode::Col => {}
                super::TriggerMode::AllNumCell => {
                    return true;
                }
                super::TriggerMode::CellInCols => {
                    return true;
                }
            }
        }
        false
    }
    pub fn has_menu_index(&self, col_name: &str, data_type: DataType, index: usize) -> bool {
        for (i, item) in self.items.iter().enumerate() {
            match item.trigger.mode {
                super::TriggerMode::Row | super::TriggerMode::Col => {}
                super::TriggerMode::AllNumCell => {
                    if data_type == DataType::Int || data_type == DataType::Float {
                        if index == i {
                            return true;
                        }
                    }
                }
                super::TriggerMode::CellInCols => {
                    if item.trigger.col_name_where_map.contains_key(col_name) {
                        if index == i {
                            return true;
                        }
                    }
                }
            }
        }
        false
    }
    pub fn get_context_menu_count(&self, col_name: &str, data_type: DataType) -> u8 {
        let mut count = 0;
        for item in self.items.iter() {
            match item.trigger.mode {
                super::TriggerMode::Row | super::TriggerMode::Col => {}
                super::TriggerMode::AllNumCell => {
                    if data_type == DataType::Int || data_type == DataType::Float {
                        count += 1;
                    }
                }
                super::TriggerMode::CellInCols => {
                    if item.trigger.col_name_where_map.contains_key(col_name) {
                        count += 1;
                    }
                }
            }
        }
        count
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ContextMenuItem {
    pub caption: String,
    pub title_columns: Vec<String>,
    pub trigger: Trigger,
    #[serde(default)]
    pub drawer: Drawer,
    pub context_actions: Vec<ContextAction>,
    #[serde(default)]
    pub is_default: bool,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum ActionKind {
    Report,
    Page,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ContextAction {
    pub kind: ActionKind,
    pub name: String,
    pub param_map: HashMap<String, String>,
    pub auth_filter: Option<AuthFilter>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Drawer {
    pub placement: DrawerPlacement,
    #[serde(default = "default_grid")]
    pub grid: u16,
}
fn default_grid() -> u16 {
    6
}
impl Default for Drawer {
    fn default() -> Self {
        Drawer {
            placement: DrawerPlacement::default(),
            grid: 6,
        }
    }
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub enum DrawerPlacement {
    Top,
    Right,
    Bottom,
    Left,
}
impl Default for DrawerPlacement {
    fn default() -> Self {
        DrawerPlacement::Right
    }
}
impl<'de> Deserialize<'de> for DrawerPlacement {
    fn deserialize<D>(deserializer: D) -> Result<DrawerPlacement, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        match s.to_lowercase().as_str() {
            "top" => Ok(DrawerPlacement::Top),
            "right" => Ok(DrawerPlacement::Right),
            "bottom" => Ok(DrawerPlacement::Bottom),
            "left" => Ok(DrawerPlacement::Left),
            _ => Err(serde::de::Error::custom("invalid DrawerPlacement")),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Trigger {
    pub mode: TriggerMode,
    #[serde(default)]
    pub col_name_where_map: HashMap<String, String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum TriggerMode {
    Row,
    AllNumCell,
    CellInCols,
    Col,
}
